package cn.openea.perpartner.componet.web.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.openea.perpartner.common.core.config.SystemConfig;
import cn.openea.perpartner.common.core.utils.DateUtils;
import cn.openea.perpartner.common.core.utils.IdUtil;
import cn.openea.perpartner.componet.util.CosUpload;
import cn.openea.perpartner.componet.web.entity.HisShareLog;
import cn.openea.perpartner.componet.web.entity.InsShareLog;
import cn.openea.perpartner.componet.web.entity.dto.ShareDto;
import cn.openea.perpartner.componet.web.service.IHisShareLogService;
import cn.openea.perpartner.componet.web.service.IShareService;
import cn.openea.perpartner.componet.web.service.InsShareLogService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.rmi.ServerException;
import java.util.Calendar;
import java.util.Date;

/**
 * @author Weird077
 * @description TODO
 * @date 2023-03-01 14:06
 */
@Slf4j
@Service
public class ShareServiceImpl implements IShareService {
    /**
     * 默认用户头像
     */
    static final String DEFAULT_URL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/perpartner.png";
    /**
     * 商品团购引导图片地址
     */
    static final  String GUIDE_URL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtext.png";
    /**
     * 帮卖海报引导图片地址
     */
    static final  String BMHB_GUIDE_URL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtextbm.png";
    /**
     * 测评海报引导图片地址
     */
    static final  String CPHB_GUIDE_URL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtextcp.png";
    /**
     * 参与活动引导图片地址
     */
    static final  String TGHD_GUIDE_URL ="https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtexthd.png";
    @Value("${tencent.applets.appid}")
    private String appid;
    @Value("${tencent.applets.secret}")
    private String secret;
    @Value("${tencent.applets.page}")
    private String page;
    @Value("${tencent.applets.env_version}")
    private String version;

    @Resource
    private CosUpload cosUpload;
    @Resource
    private IHisShareLogService hisShareService;
    @Resource
    private InsShareLogService insShareLogService;

    @Override
    public String linkShare(ShareDto shareDto) {
        InsShareLog insShareLog = new InsShareLog();
        long shareId = IdUtil.nextId();
        insShareLog.setId(shareId);
        insShareLog.setType(shareDto.getShareType());
        insShareLog.setSourceId(shareDto.getSourceId());
        insShareLog.setInviter(shareDto.getUserId());
        insShareLog.setInviterName(shareDto.getNickName());
        String params = "?type=" + shareDto.getShareType() + "&shareId="+shareId;
        String shareUrl = page + params;
        insShareLog.setShareAddress(shareDto.getShareSource());
        insShareLog.setShareSource(shareUrl);
        long expireTime = new Date(System.currentTimeMillis() + (3 * 1000 * 60 * 60 * 24)).getTime();
        insShareLog.setExpireTime(Convert.toStr(expireTime));
        insShareLog.setCreateTime(DateUtil.now());
        return insShareLogService.save(insShareLog)?shareUrl:"";
    }
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    @Override
    public String codeShare(ShareDto shareDto) throws IOException {
        long shareId = IdUtil.nextId();
        String paramArgs = shareDto.getShareType()+"#"+shareId;
        if(paramArgs.length() > 32){
            int length = 32 - (paramArgs.length() + 1);
            throw new ServerException("type参数最多允许("+length+"）位，当前type参数过长使菊花码参数超过限制");
        }
        //生成小程序二维码
        BufferedImage qrcodeImg = generateQrCode(paramArgs,false);
        //将创建的菊花码在本地生成文件
        File uploadfile = getWriteLocalFile(shareDto.getUserId(), qrcodeImg);
        //将图片上传到存储桶并返回URL
        String cosUrl = cosUpload.uploadFile(uploadfile, true);
        //生成分享记录
        saveShareLog(shareDto,shareId,Convert.toLong(getExpireTime(1)));
        //删除本地文件
        FileUtil.del(uploadfile);
        return cosUrl;

    }
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    @Override
    public String postersShare(ShareDto shareDto) throws IOException {
        long shareId = IdUtil.nextId();
        String paramArgs = shareDto.getShareType()+"#"+shareId;
        if(paramArgs.length() > 32){
            int length = 32 - (paramArgs.length() + 1);
            throw new ServerException("type参数最多允许("+length+"）位，当前type参数过长使菊花码参数超过限制");
        }
        // 商品主图URL地址
        String imageURL = shareDto.getImageUrl();
        // 商品名称
        String productName = shareDto.getProductName();
        // 商品价格
        String productPrice = shareDto.getProductPrice();
        // 邀请内容 例如是 邀请购买商品 还是 邀请参与测评，可不传，不传时默认为 ”邀请”两个字，传了就显示传的的内容
        String inviteMsg = shareDto.getInviteMsg();
        // 加一个参数，type 类别 type 为空，表示商品专属海报，type=bmhb 就生成帮卖海报，type=cphb 就生成测评海报，type=tghd就是邀请参与团购活动
        String type = shareDto.getShareType();
        //生成小程序二维码
        BufferedImage qrcodeImg = generateQrCode(paramArgs,true);
        //创建分享模板图片海报
        BufferedImage shareImg = createSharePicture(shareDto, qrcodeImg, imageURL, productName, productPrice, inviteMsg, type);
        //将创建的海报在本地生成文件
        File uploadfile = getWriteLocalFile(shareDto.getUserId(), shareImg);
        //将图片上传到存储桶并返回URL
        String cosUrl = cosUpload.uploadFile(uploadfile, true);
        //生成分享记录
        saveShareLog(shareDto,shareId,Convert.toLong(getExpireTime(1)));
        //删除本地文件
        FileUtil.del(uploadfile);
        return cosUrl;
    }

    @Override
    public String findById(String shareId) {
        InsShareLog insShareLog = insShareLogService.getById(shareId);
        if(BeanUtil.isNotEmpty(insShareLog)){
            return insShareLog.getShareSource();
        }
        HisShareLog hisShareLog = hisShareService.getById(shareId);
        if(BeanUtil.isNotEmpty(insShareLog)){
            return hisShareLog.getShareSource();
        }
        return "";
    }

    /**
     * 生成分享记录
     * @param shareDto 分享信息
     * @param shareId 分享ID
     * @param expireTime 有效期
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    void saveShareLog(ShareDto shareDto, long shareId, long expireTime){
        InsShareLog insShareLog = new InsShareLog();
        insShareLog.setId(shareId);
        insShareLog.setSourceId(shareDto.getSourceId());
        insShareLog.setType(shareDto.getShareType());
        insShareLog.setSourceId(shareDto.getSourceId());
        insShareLog.setInviter(shareDto.getUserId());
        insShareLog.setInviterName(shareDto.getNickName());
        String params = "?type=" + shareDto.getShareType() + "&shareId="+shareId;
        insShareLog.setShareAddress(shareDto.getShareSource());
        String shareSource = page + params;
        if (StrUtil.isNotBlank(shareDto.getShareSource())){
            shareSource = shareDto.getShareSource();
        }
        insShareLog.setShareSource(shareSource);
        insShareLog.setExpireTime(Convert.toStr(expireTime));
        insShareLog.setCreateTime(DateUtil.now());
        insShareLogService.save(insShareLog);
    }
    /**
     * 将当前时间向前加 year 年的 TimeInMillis 数
     * @param year 需要增加的年数，至少1年起步
     * @return 增加年数后反回的 TimeInMillis
     */
    private String getExpireTime(int year) {
        if(year == 0){
            year = 1;
        }
        Calendar canlendar = Calendar.getInstance();
        canlendar.add(Calendar.YEAR, year);
        return String.valueOf(canlendar.getTimeInMillis());
    }
    /**
     * 生成小程序二维码
     * page  当前小程序相对页面 必须是已经发布的小程序存在的页面（否则报错），例如 pages/index/index,
     * 根路径前不要填加 /,不能携带参数（参数请放在scene字段里），如果不填写这个字段，默认跳主页面
     * @param scene 最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&'()*+,/:;=?@-._~，
     *              其它字符请自行编码为合法字符（因不支持%，中文无法使用 urlencode 处理，请使用其他编码方式）
     */
    private BufferedImage generateQrCode(String scene,boolean isHyaline) {
        BufferedImage qrcodeImg = null;
        // 发起API请求，返回对应的参数信息
        try {
            // 获取小程序二维码的API地址，带参数
            String REQURL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken();
            //调用微信接口生成二维码
            URL url = new URL(REQURL);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 提交模式
            connection.setRequestMethod("POST");
            // 发送POST请求必须设置如下两行
            connection.setDoOutput(true);
            connection.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
            // 小程序二维码的API请求时的参数
            JSONObject paramJson = new JSONObject();
            //这就是你二维码里携带的参数 String型  名称不可变
            paramJson.put("scene", scene);
            //注意该接口传入的是page而不是path
            paramJson.put("page", page);
            //这是设置扫描二维码后跳转的页面
            paramJson.put("width", 200);
            paramJson.put("is_hyaline", isHyaline);
            paramJson.put("auto_color", true);
            paramJson.put("env_version", version);
            printWriter.write(paramJson.toString());
            // flush输出流的缓冲
            printWriter.flush();
            //开始获取数据
            BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
            qrcodeImg = ImageIO.read(bis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return qrcodeImg;
    }

    /**
     * 获取小程序access_token
     **/
    private String getAccessToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
        String result = HttpUtil.get(url);
        if (StrUtil.isNotBlank(result)) {
            JSONObject wxObject = JSON.parseObject(result);
            return wxObject.getString("access_token");
        }
        return "";
    }

    /**
     * 创建分享模板图片海报
     * @param shareDto      用户信息，主要用于取昵称和头像地址
     * @param qrcodeImg    生成的小程序菊花码图片
     * @param imageURL     商品主图URL地址
     * @param productName  商品名称
     * @param productPrice 商品价格，不用写￥
     * @param invitemag    邀请内容
     * @param type         海报类型
     * @return BufferedImage图片流
     */
    private BufferedImage createSharePicture(ShareDto shareDto, BufferedImage qrcodeImg, String imageURL,
                                             String productName, String productPrice, String invitemag, String type) throws IOException {
        BufferedImage shareImg = new BufferedImage(630, 1012, BufferedImage.TYPE_INT_RGB);
        String nickName = shareDto.getNickName();
        String avatarUrl = shareDto.getAvatarUrl();
        String dateStr = DateUtils.formatDate(new Date(), "MM/dd HH:mm:ss");
        // 长按二维码提示URL地址
        String guideUrl = GUIDE_URL;
        // type=bmhb 就生成 帮卖海报
        if("bmhb".equalsIgnoreCase(type)){
            guideUrl = BMHB_GUIDE_URL;
        } else if("cphb".equalsIgnoreCase(type)){
            guideUrl = CPHB_GUIDE_URL;
        } else if("tghd".equalsIgnoreCase(type)){
            guideUrl = TGHD_GUIDE_URL;
        }
        BufferedImage userImage;
        try{
            // 如果头像地址为空，则
            userImage = ImageIO.read(new URL(avatarUrl));
        } catch (Exception e) {
            userImage = ImageIO.read(new URL(DEFAULT_URL));
        }
        // 读入商品主图片
        BufferedImage productImage = ImageIO.read(new URL(imageURL));
        // 长按二维码
        BufferedImage guideImage = ImageIO.read(new URL(guideUrl));
        // 以背景的图片做为底图
        Graphics graph = shareImg.createGraphics();
        //设置颜色
        graph.setColor(Color.WHITE);
        //将设置的颜色设置为背景色
        graph.fillRect(0, 0, 630, 1012);
        // 头像旁边的昵称信息 25，BLACK 原坐标：37，这里要重新设置字体颜色，否则和背景相同颜色会显示不出来
        graph.setColor(Color.BLACK);
        Font font = new Font("Microsoft YaHei Light", Font.BOLD, 23);
        graph.setFont(font);
        graph.drawString(nickName + (StrUtil.isBlank(invitemag)?"邀请":invitemag), 117, 60);
        // 头像旁边的时间信息 20，LIGHT_GRAY  原坐标：75  微软雅黑
        graph.setColor(Color.LIGHT_GRAY);
        font = new Font("Microsoft YaHei Light", Font.BOLD, 18);
        graph.setFont(font);
        graph.drawString(dateStr, 117, 90);
        // 主图下的商品标题，38 BLACK  原坐标：734  , fontsize=25
        graph.setColor(Color.BLACK);
        font = new Font("Microsoft YaHei Light", Font.BOLD, 29);
        graph.setFont(font);
        // 通过JLabel来计算文本的总长度px
        JLabel jLabel = new JLabel(productName);
        jLabel.setFont(font);
        FontMetrics fontMetrics = jLabel.getFontMetrics(font);
        int textLength = productName.length();
        //一行的总长度，用于判断是否超出了范围
        int totalWidth = fontMetrics.stringWidth(productName);
        //计算一行的高度
        int textHeight = fontMetrics.getHeight();
        //行间距
        int heightSpace = 5;
        int maxWidth = shareImg.getWidth()-40;
        if (totalWidth > maxWidth) {
            // 如果总长度超过了整个长度限制
            //已经写了多少行
            int alreadyWriteLine = 0;
            //目前一行写的长度
            int nowWidth = 0;
            for (int i = 0; i < textLength; i++) {
                //获取单个字符的长度
                int oneWordWidth = fontMetrics.charWidth(productName.charAt(i));
                //判断目前的一行加上这个字符的长度是否超出了总长度
                int tempWidth = oneWordWidth + nowWidth;
                if (tempWidth > maxWidth) {
                    // 如果超出了一行的总长度，则要换成下一行
                    nowWidth = 0;
                    alreadyWriteLine++;
                    int writeY = 744 + alreadyWriteLine * (textHeight + heightSpace);
                    graph.drawString(productName.charAt(i) + "", 30 + nowWidth, writeY);
                    nowWidth = oneWordWidth;
                } else {
                    // 当前行长度足够，可以直接画
                    int writeY = 744 + alreadyWriteLine * (textHeight + heightSpace);
                    graph.drawString(productName.charAt(i) + "", 30 + nowWidth, writeY);
                    nowWidth = tempWidth;
                }
            }
        } else {
            // 如果没有超过限制，直接画
            graph.drawString(productName, 30, 744);
        }
        graph.setColor(Color.RED);
        font = new Font(null, Font.BOLD, 25);
        graph.setFont(font);
        if("bmhb".equalsIgnoreCase(type)){
            // 帮卖海报显示
            graph.drawString("￥ ", 33, 870);
            font = new Font(null, Font.BOLD, 38);
            graph.setFont(font);
            graph.drawString(productPrice + "销售报酬", 60, 870);
        } else if("tghd".equalsIgnoreCase(type)) {
            // 参与活动显示
            graph.drawString("￥ ", 33, 870);
            font = new Font(null, Font.BOLD, 38);
            graph.setFont(font);
            graph.drawString(productPrice + "玫丽专享价", 60, 870);
        } else {
            // 这里除了显示团购海报外，还负责显示 测评海报
            // 普通商品海报显示：商品价格 20，RED  原坐标：842
            graph.drawString("￥ ", 33, 870);
            font = new Font(null, Font.BOLD, 38);
            graph.setFont(font);
            graph.drawString(productPrice, 60, 870);
        }
        // 绘制商品主图片
        graph.drawImage(productImage.getScaledInstance(580, 580, Image.SCALE_DEFAULT), 25, 130, null);
        // 绘制小程序二维码
        graph.drawImage(qrcodeImg.getScaledInstance(190, 190, Image.SCALE_DEFAULT), 425, 797, null);
        // 绘制长按二维码
        graph.drawImage(guideImage.getScaledInstance(330, 60, Image.SCALE_DEFAULT), 33, 897, null);
        // 头像图片是一个圆型，并定位
        Ellipse2D.Double shape = new Ellipse2D.Double(25, 25, 80, 80);
        // 需要保留的区域
        graph.setClip(shape);
        // 再绘制头像到背景底图的顶层
        graph.drawImage(userImage, 25, 25, 80, 80, null);

        graph.dispose();
        return shareImg;
    }

    /**
     * 将创建的海报在本地生成文件
     * @param userid 用户id，以用户id作为文件的名字
     * @param shareImg 需要生成海报的源
     * @return 生成的文件
     */
    private File getWriteLocalFile(String userid, BufferedImage shareImg) throws IOException {
        String basePath = SystemConfig.getProfile() + "/shareCode/" + DateUtil.format(new Date(), "yyMM") + "/";
        File file = new File(basePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        String filepath = basePath + userid + ".jpg";
        file = new File(filepath);
        // 将推广图片写入本地地址
        ImageIO.write(shareImg, "jpg", file);
        return file;
    }

}
